/**
 * @author Fabien BIZOT
 * http://lafabrick.com/blog
 * http://twitter.com/fabienbizot
 */
package com.lafabrick.eaze
{
	import aze.motion.EazeTween;
	import aze.motion.easing.Quadratic;
	import aze.motion.eaze;
	
	import com.lafabrick.eaze.commands.EazeApply;
	import com.lafabrick.eaze.commands.EazeChain;
	import com.lafabrick.eaze.commands.EazeDelay;
	import com.lafabrick.eaze.commands.EazeFrom;
	import com.lafabrick.eaze.commands.EazeTo;
	import com.lafabrick.eaze.commands.IEazeCommand;
	
	import flash.events.Event;
	
	import mx.effects.Effect;
	import mx.effects.EffectTargetFilter;
	import mx.effects.IEffect;
	import mx.effects.IEffectInstance;
	import mx.effects.IEffectTargetHost;
	import mx.events.EffectEvent;
	
	// Need to excule some property and method define in the IEffect interface....
	// It's not really clean... so you can vote on the flex bug base for the refactorisation of effect interfaces here : 
	// http://bugs.adobe.com/jira/browse/SDK-25769
	
	// Exclusion of all property define by the flex interface IEffect, but not used by the eaze wrapper
	[Exclude(name="className", kind="property")]
	[Exclude(name="customFilter", kind="property")]
	[Exclude(name="effectTargetHost", kind="property")]
	[Exclude(name="filter", kind="property")]
	[Exclude(name="hideFocusRing", kind="property")]
	[Exclude(name="perElementOffset", kind="property")]
	[Exclude(name="relevantProperties", kind="property")]
	[Exclude(name="relevantStyles", kind="property")]
	[Exclude(name="duration", kind="property")]
	[Exclude(name="targets", kind="property")]
	[Exclude(name="getAffectedProperties", kind="property")]
	[Exclude(name="triggerEvent", kind="property")]
	[Exclude(name="playheadTime", kind="property")]
	[Exclude(name="repeatCount", kind="property")]
	
	// Exclusion of all property define by the object Effect, but not used by the eaze wrapper
	[Exclude(name="instanceClass", kind="property")]
	[Exclude(name="repeatDelay", kind="property")]
	[Exclude(name="startDelay", kind="property")]
	
	// Exclusion of all method define by the flex interface IEffect, but not used by the eaze wrapper
	[Exclude(name="reverse", kind="method")]
	[Exclude(name="captureStartValues", kind="method")]
	[Exclude(name="captureMoreStartValues", kind="method")]
	[Exclude(name="captureEndValues", kind="method")]
	[Exclude(name="getAffectedProperties", kind="method")]
	[Exclude(name="createInstances", kind="method")]
	[Exclude(name="createInstance", kind="method")]
	[Exclude(name="deleteInstance", kind="method")]
	[Exclude(name="end", kind="method")]
	
	// adding EFFECT_UPDATE Event that is not throw by default Effect Object
	/**
	 * Dispatched when the effect updating
	 */
	[Event(name="effectUpdate", type="mx.events.EffectEvent")]
	
	/**
	 * Dispatched when the effect starts playing
	 */
	[Event(name="effectStart", type="mx.events.EffectEvent")]
	
	/**
	 * Dispatched when the effect has been stopped
	 */
	[Event(name="effectStop", type="mx.events.EffectEvent")]
	
	/**
	 * Dispatched when the effect finishes playing
	 */
	[Event(name="effectEnd", type="mx.events.EffectEvent")]
	
	/**
	 * EazeFx : Flex / AIR wrapper for the eaze tween engine
	 */
	public class EazeFx extends Effect implements IEffect
	{
		/**
		 * Define the string type of "apply" command
		 */
		public static const EAZE_APPLY : String = "eazeApply";
		/**
		 * Define the string type of "to" command
		 */
		public static const EAZE_TO : String = "eazeTo";
		/**
		 * Define the string type of "from" command
		 */
		public static const EAZE_FROM : String = "eazeFrom";
		/**
		 * Define the string type of "delay" command
		 */
		public static const EAZE_DELAY : String = "eazeDelay";
		/**
		 * Define the string type of "chain" command
		 */
		public static const EAZE_CHAIN : String = "eazeChain";
		
		private var _eaze : EazeTween;
		
		private var _target : Object;
		//private var _duration : Number = 1;
		
		private var _isPlaying : Boolean = false;
		private var _easing : Function = Quadratic.easeOut;
		private var _commands : Vector.<IEazeCommand>;

		/**
		 * Vector of all commands defining the eaze tween (based on IEazeCommand interface
		 * @see com.lafabrick.eaze.commands.IEazeCommand
		 */
		public function get commands():Vector.<IEazeCommand>
		{
			return _commands;
		}
		/**
		 * @private
		 */
		public function set commands(value:Vector.<IEazeCommand>):void
		{
			_commands = value;
		}
		/**
		 * Define the target Object for this effect
		 */
		override public function get target():Object
		{
			return _target;
		}
		/**
		 * @private
		 */
		override public function set target(value:Object):void
		{ 
			_target = value; 
		}

		/**
		 * Define the starting easing function
		 */
		public function get easing():Function
		{
			return _easing;
		}
		/**
		 * @private
		 */
		public function set easing(value:Function):void
		{
			_easing = value;
		}
		
		/**
		 * Return if the tween is playing
		 * @return Boolean
		 */
		override public function get isPlaying():Boolean
		{
			return _isPlaying;
		}
		
		/**
		 * Apply a command to the current EazeTween
		 * @param command current command
		 * @param lastEaze 
		 */
		protected function obtainEazeFromCommand( command : IEazeCommand, lastEaze : EazeTween ) : EazeTween
		{
			
			switch( command.type ) {
				case EAZE_APPLY :
					lastEaze = lastEaze.apply( EazeApply(command).properties, EazeApply(command).overwrite );
					break;
				case EAZE_TO :
					lastEaze = lastEaze.to( EazeTo(command).duration, EazeTo(command).properties, EazeTo(command).overwrite );
					break;
				case EAZE_FROM :
					lastEaze = lastEaze.from( EazeFrom(command).duration, EazeFrom(command).properties, EazeFrom(command).overwrite );
					break;
				case EAZE_DELAY :
					lastEaze = lastEaze.delay( EazeDelay(command).duration, EazeDelay(command).overwrite );
					break;
				case EAZE_CHAIN :
					lastEaze = lastEaze.chain( EazeChain(command).target );
					if( EazeChain(command).easing != null ) {
						lastEaze = lastEaze.easing( EazeChain(command).easing );
					}
					break;
			}
			return lastEaze;
		}
		
		/**
		 * launch the eaze tween.
		 * Parameters are not taken into account (play method need to follow the IEffect interface, but parameters are not useful for this wrapper)
		 * @return null
		 */
		override public function play(targets:Array=null, playReversedFromEnd:Boolean=false):Array
		{
			EazeTween.killAllTweens();
			
			if( target ) {
				
				_eaze = eaze( target ).easing( easing ).onStart( onEazestart ).onComplete( onEazeEnd ).onUpdate( onEazeUpdate );
				
				for each( var command : IEazeCommand in commands ) {
					_eaze = obtainEazeFromCommand( command, _eaze );
				}
			}
			return null;
		}
		
		/**
		 * Temporarily stop all tweens
		 */
		override public function pause():void
		{
			if( isPlaying ) {
				
				EazeTween.pauseAllTweens();
				dispatchEvent(new EffectEvent(EffectEvent.EFFECT_STOP, false, false, null));
				_isPlaying = false;
			}
		}
		
		/**
		 * Stop immediately all tweens associated with target
		 */
		override public function stop():void
		{
			EazeTween.killTweensOf( target );
			dispatchEvent(new EffectEvent(EffectEvent.EFFECT_STOP, false, false, null));
			_isPlaying = false;
			
		}
		
		/**
		 * Reactivate tweens
		 */
		override public function resume():void
		{
			EazeTween.resumeAllTweens();
			_isPlaying = true;
		}
		/**
		 * @private
		 */
		protected function onEazeUpdate( ) : void
		{
			dispatchEvent(new EffectEvent(EffectEvent.EFFECT_UPDATE, false, false, null));
		}
		/**
		 * @private
		 */
		protected function onEazestart( )  : void
		{
			_isPlaying = true;
			dispatchEvent(new EffectEvent(EffectEvent.EFFECT_START, false, false, null));
		}
		/**
		 * @private
		 */
		protected function onEazeEnd( )  : void
		{
			_isPlaying = false;
			dispatchEvent(new EffectEvent(EffectEvent.EFFECT_END, false, false, null));
		}
		
// --------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------
		/**
		 * &private
		 */
		override public function get className():String{return null;}
		
		override public function get customFilter():EffectTargetFilter{return null;}
		override public function set customFilter(value:EffectTargetFilter):void{}
		
		override public function get effectTargetHost():IEffectTargetHost{return null;}
		override public function set effectTargetHost(value:IEffectTargetHost):void{}
		
		override public function get filter():String{return null;}
		override public function set filter(value:String):void{}
		
		override public function get hideFocusRing():Boolean{return false;}
		override public function set hideFocusRing(value:Boolean):void{}
		
		override public function get perElementOffset():Number{return 0;}
		override public function set perElementOffset(value:Number):void{}
		
		override public function get relevantProperties():Array{return null;}
		override public function set relevantProperties(values:Array):void{}
		
		override public function get relevantStyles():Array{return null;}
		override public function set relevantStyles(values:Array):void{}
		
		override public function get targets():Array{return null;}
		override public function set targets(value:Array):void{}

		override public function get triggerEvent():Event{return null;}
		override public function set triggerEvent(value:Event):void{}
		
		// call by UIComponent to calcultate
		override public function set playheadTime(value:Number):void{}
		override public function get playheadTime():Number{return 0;}
		
		override public function reverse():void{}
		
		// call by UIComponent to get start values
		override public function captureStartValues():void{}
		
		override public function captureMoreStartValues(targets:Array):void{}
		
		override public function captureEndValues():void{}
		
		override public function getAffectedProperties():Array{return null;}
		
		override public function createInstances(targets:Array=null):Array{return null;}
		
		override public function createInstance(target:Object=null):IEffectInstance{return null;}
		
		override public function deleteInstance(instance:IEffectInstance):void{}
		
		// call by UIComponent for close the effect instance
		override public function end(effectInstance:IEffectInstance=null):void{}
		
	}
}